---
title: Create a Token Account
description:
  Learn how to create and initialize token accounts in Solana programs using
  Anchor. Covers creating Associated Token Accounts (ATAs) and Program Derived
  Address (PDA) token accounts with code examples.
---

## What is a Token Account?

A token account is an account type in Solana's Token Programs that stores
information about an individual's ownership of a specific token (mint). Each
token account is associated with a single mint and tracks details like the token
balance and owner.

```rust
/// Account data.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Account {
    /// The mint associated with this account
    pub mint: Pubkey,
    /// The owner of this account.
    pub owner: Pubkey,
    /// The amount of tokens this account holds.
    pub amount: u64,
    /// If `delegate` is `Some` then `delegated_amount` represents
    /// the amount authorized by the delegate
    pub delegate: COption<Pubkey>,
    /// The account's state
    pub state: AccountState,
    /// If `is_native.is_some`, this is a native token, and the value logs the
    /// rent-exempt reserve. An Account is required to be rent-exempt, so
    /// the value is used by the Processor to ensure that wrapped SOL
    /// accounts do not drop below this threshold.
    pub is_native: COption<u64>,
    /// The amount delegated
    pub delegated_amount: u64,
    /// Optional authority to close the account.
    pub close_authority: COption<Pubkey>,
}
```

<Callout type="info">
  Note that in the source code, a Token account is referred to as an `Account`
  type. Both the [Token
  Program](https://github.com/solana-program/token/blob/main/program/src/state.rs#L89-L110)
  and [Token Extension
  Program](https://github.com/solana-program/token-2022/blob/main/program/src/state.rs#L104-L124)
  have the same base implementation for the Token account.
</Callout>

To hold tokens for a specific mint, users must first create a token account.
Each token account is associated with:

1. A specific mint (the token type the token account holds units of)
2. An owner (the authority who can transfer tokens from the account)

Let's look at an example using USDC on Solana:

- The USDC mint address is `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v`
- Circle (the USDC issuer) has a token account at
  `3emsAVdmGKERbHjmGfQ6oZ1e35dkf5iYcS6U4CPKFVaa`
- This token account can only hold units of the USDC token (mint)
- Circle is set as the owner at `7VHUFJHWu2CuExkJcJrzhQPJ2oygupTWkL2A2For4BmE`
  and can transfer these tokens

You can view the details of this token account on
[Solana Explorer](https://explorer.solana.com/address/3emsAVdmGKERbHjmGfQ6oZ1e35dkf5iYcS6U4CPKFVaa).

<Callout type="info">
  The term "owner" is used in two different contexts:

1. The token account "owner" - This is an address stored in the token account's
   as the "owner" field of the `Account` type defined by the Token Program. The
   owner can transfer, burn, or delegate tokens from the account. This address
   is sometimes referred to as the "authority" of the token account to
   distinguish it from the program owner.

2. The program "owner" - This refers to the program that owns the account data
   on Solana. For token accounts, this is always either the Token Program or
   Token Extension Program, as specified in the "owner" field of the base Solana
   [Account](https://github.com/anza-xyz/agave/blob/master/sdk/account/src/lib.rs#L44-L56)
   type.

When working with token accounts, "owner" typically refers to the authority that
can spend the tokens, not the program that owns the account.

</Callout>

## What is an Associated Token Account?

An associated token account (ATA) is simply a token account with an address that
is a PDA derived from and created by the
[Associated Token Program](https://github.com/solana-program/associated-token-account/tree/main/program).
You can think of an ATA as the default token account for a user to hold units of
a specific token (mint).

<Callout type="info">
  Only token accounts created through the Associated Token Program are referred
  to as associated token accounts.
</Callout>

ATAs provide a deterministic way to find a user's token account for any given
mint. You can inspect the implementation of the derivation
[here](https://github.com/solana-program/associated-token-account/blob/main/interface/src/address.rs#L56-L70).

```rust title="Associated Token Account Address Derivation"
pub fn get_associated_token_address_and_bump_seed_internal(
    wallet_address: &Pubkey,
    token_mint_address: &Pubkey,
    program_id: &Pubkey,
    token_program_id: &Pubkey,
) -> (Pubkey, u8) {
    Pubkey::find_program_address(
        &[
            &wallet_address.to_bytes(), // Owner's public key
            &token_program_id.to_bytes(), // Token Program or Token Extension Program
            &token_mint_address.to_bytes(), // Token mint address
        ],
        program_id, // Associated Token Program ID
    )
}
```

This deterministic derivation ensures that for any combination of wallet address
and token mint, there exists exactly one associated token account address. This
approach makes it simple to find a user's token account for any given token
mint, eliminating the need to track token account addresses separately.

<Callout type="info">
  The Associated Token Program acts as a helper program that creates token
  accounts with deterministic addresses (PDAs). When creating an associated
  token account, the Associated Token Program makes a CPI (Cross-Program
  Invocation) to either the Token Program or Token Extension Program. The
  created account is owned by the token program and has the same `Account` type
  structure as defined in the token program. The Associated Token Program itself
  maintains no state - it simply provides a standardized way to create token
  accounts at a deterministic address.
</Callout>

## Usage

Use the `token_interface` and `associated_token` modules from the `anchor-spl`
crate to work with ATAs compatible with either the Token Program and Token
Extension Program.

```rust title="snippet"
// [!code highlight:2]
use anchor_spl::associated_token::AssociatedToken;
use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface};

// --snip--

#[derive(Accounts)]
pub struct CreateTokenAccount<'info> {
    #[account(mut)]
    pub signer: Signer<'info>,
    #[account(
        init_if_needed,
        payer = signer,
        associated_token::mint = mint,
        associated_token::authority = signer,
        associated_token::token_program = token_program,
    )]
    // [!code highlight:4]
    pub token_account: InterfaceAccount<'info, TokenAccount>,
    pub mint: InterfaceAccount<'info, Mint>,
    pub token_program: Interface<'info, TokenInterface>,
    pub associated_token_program: Program<'info, AssociatedToken>,
    pub system_program: Program<'info, System>,
}
```

To create token accounts with PDAs derived from your program, you can use the
`token::mint`, `token::authority`, and `token::token_program` constraints along
with the `seeds` and `bump` constraints.

```rust title="snippet"
use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface};

// --snip--

#[derive(Accounts)]
pub struct CreateTokenAccount<'info> {
    #[account(mut)]
    pub signer: Signer<'info>,
    #[account(
        // [!code word:seeds]
        // [!code word:bump]
        // [!code highlight:7]
        init_if_needed,
        payer = signer,
        token::mint = mint,
        token::authority = token_account,
        token::token_program = token_program,
        seeds = [b"token"],
        bump
    )]
    pub token_account: InterfaceAccount<'info, TokenAccount>,
    pub mint: InterfaceAccount<'info, Mint>,
    pub token_program: Interface<'info, TokenInterface>,
    pub system_program: Program<'info, System>,
}
```

### Account Types

The
[`InterfaceAccount`](https://github.com/coral-xyz/anchor/blob/0e5285aecdf410fa0779b7cd09a47f235882c156/lang/src/accounts/interface_account.rs#L161-L166)
type is a wrapper that allows the account to work with both the Token Program
and Token Extension Program.

The `TokenAccount` type represents the base `Account` data structure shared by
both token programs. When an account of this type is passed in, Anchor will
automatically deserialize the account data into the `Account` struct, regardless
of which token program created it.

```rust title="Account Type"
pub token_account: InterfaceAccount<'info, TokenAccount>,
```

### Account Constraints

Anchor provides two sets of constraints for working with token accounts:

- Use `associated_token` constraints when working with Associated Token Accounts
  (ATAs)
- Use `token` constraints when working with token accounts that are not
  specifically ATAs, such as custom PDAs or token accounts with addresses that
  are public keys from a keypair

The appropriate constraint to use depends on your specific use case. ATAs are
recommended for user wallets, while custom token accounts are useful for program
controlled accounts.

#### `associated_token` constraints

The following account constraints are used in combination to create and
initialize a new associated token account:

| Constraint                        | Description                                                                                                                                                                                               |
| --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `init`                            | Creates a new account by making a cross program invocation (CPI) to the System Program. This allocates the required space for the token account and transfers ownership to the appropriate token program. |
| `init_if_needed`                  | Similar to `init`, but only creates the account if it doesn't already exist. Requires enabling the `init-if-needed` feature.                                                                              |
| `payer`                           | Specifies which account will pay the rent (SOL deposit) required to create the new account.                                                                                                               |
| `associated_token::mint`          | Specifies the mint account that this token account will be associated with.                                                                                                                               |
| `associated_token::authority`     | Sets the authority (owner) of the token account who has permission to transfer or burn tokens.                                                                                                            |
| `associated_token::token_program` | Specifies which token program (Token Program or Token Extension Program) to use when creating the token account.                                                                                          |

```rust title="Create Associated Token Account"
#[account(
    init,
    payer = <payer>,
    associated_token::mint = <mint>,
    associated_token::authority = <authority>,
    associated_token::token_program = <token_program>
)]
pub token_account: InterfaceAccount<'info, TokenAccount>,
```

#### `token` constraints

The following account constraints are used in combination to create and
initialize a new token account:

| Constraint             | Description                                                                                                                                                                                               |
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `init`                 | Creates a new account by making a cross program invocation (CPI) to the System Program. This allocates the required space for the token account and transfers ownership to the appropriate token program. |
| `init_if_needed`       | Similar to `init`, but only creates the account if it doesn't already exist. Requires enabling the `init-if-needed` feature.                                                                              |
| `payer`                | Specifies which account will pay the rent (SOL deposit) required to create the new account.                                                                                                               |
| `token::mint`          | Specifies the mint account that this token account will be associated with.                                                                                                                               |
| `token::authority`     | Sets the authority (owner) of the token account who has permission to transfer or burn tokens.                                                                                                            |
| `token::token_program` | Specifies which token program (Token Program or Token Extension Program) to use when creating the token account.                                                                                          |

```rust title="Create Token Account with Keypair Public Key as Address"
#[account(
    init,
    payer = <payer>,
    token::mint = <mint>,
    token::authority = <authority>,
    token::token_program = <token_program>
)]
pub token_account: InterfaceAccount<'info, TokenAccount>,
```

```rust title="Create Token Account with PDA as Address"
#[account(
    init,
    payer = <payer>,
    token::mint = <mint>,
    token::authority = <authority>,
    token::token_program = <token_program>,
    seeds = [<seeds>],
    bump
)]
pub token_account: InterfaceAccount<'info, TokenAccount>,
```

<Callout type="info">
  Note that you can use the same PDA as both the `token::authority` and the
  token account address. Using a PDA as the `token::authority` enables your
  program to "sign" CPI instructions to transfer tokens from the token account.
  This pattern allows for a single deterministic address for both purposes.
</Callout>

To use the `init_if_needed` constraint, enable the `init-if-needed` feature in
`Cargo.toml` and replace the `init` constraint with `init_if_needed`.

```toml title="Cargo.toml"
[dependencies]
anchor-lang = { version = "0.32.1", features = ["init-if-needed"] }
```

## Examples

The following examples demonstrate how to create a token account in an Anchor
program using two different approaches:

1. Using an Associated Token Account (ATA) - This is the standard approach to
   create a token account for a specific user to hold units of a specific token
   (mint).

2. Using a Program Derived Address (PDA) - This approach creates a token account
   where the address is a custom PDA. This allows for deterministic token
   account addresses specific to your program. You can also set the authority
   (owner) as a PDA to enable your program to transfer tokens from the token
   account.

Both approaches are can be done entirely using account constraints.

### Create Associated Token Account

Create an associated token account for a user.

<Tabs items={["Program", "Client"]}>
<Tab value="Program">

```rust title="lib.rs"
use anchor_lang::prelude::*;
use anchor_spl::associated_token::AssociatedToken;
use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface};

declare_id!("3pX5NKLru1UBDVckynWQxsgnJeUN3N1viy36Gk9TSn8d");

#[program]
pub mod token_example {
    use super::*;

    pub fn create_mint(ctx: Context<CreateMint>) -> Result<()> {
        msg!("Created Mint Account: {:?}", ctx.accounts.mint.key());
        Ok(())
    }

    pub fn create_token_account(ctx: Context<CreateTokenAccount>) -> Result<()> {
        msg!(
            "Created Token Account: {:?}",
            ctx.accounts.token_account.key()
        );
        Ok(())
    }
}

#[derive(Accounts)]
pub struct CreateMint<'info> {
    #[account(mut)]
    pub signer: Signer<'info>,
    #[account(
        init,
        payer = signer,
        mint::decimals = 6,
        mint::authority = mint.key(),
        mint::freeze_authority = mint.key(),
        seeds = [b"mint"],
        bump
    )]
    pub mint: InterfaceAccount<'info, Mint>,
    pub token_program: Interface<'info, TokenInterface>,
    pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct CreateTokenAccount<'info> {
    #[account(mut)]
    pub signer: Signer<'info>,
    #[account(
        // [!code highlight:5]
        init_if_needed,
        payer = signer,
        associated_token::mint = mint,
        associated_token::authority = signer,
        associated_token::token_program = token_program,
    )]
    pub token_account: InterfaceAccount<'info, TokenAccount>,
    pub mint: InterfaceAccount<'info, Mint>,
    pub token_program: Interface<'info, TokenInterface>,
    pub associated_token_program: Program<'info, AssociatedToken>,
    pub system_program: Program<'info, System>,
}
```

</Tab>
<Tab value="Client">

```ts title="test.ts"
import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { TokenExample } from "../target/types/token_example";
import {
  TOKEN_2022_PROGRAM_ID,
  getAssociatedTokenAddress,
  getMint,
  getAccount,
} from "@solana/spl-token";

describe("token-example", () => {
  anchor.setProvider(anchor.AnchorProvider.env());

  const program = anchor.workspace.TokenExample as Program<TokenExample>;
  const [mint, bump] = anchor.web3.PublicKey.findProgramAddressSync(
    [Buffer.from("mint")],
    program.programId,
  );

  it("Is initialized!", async () => {
    const tx = await program.methods
      .createMint()
      .accounts({
        tokenProgram: TOKEN_2022_PROGRAM_ID,
      })
      .rpc({ commitment: "confirmed" });
    console.log("Your transaction signature", tx);

    const mintAccount = await getMint(
      program.provider.connection,
      mint,
      "confirmed",
      TOKEN_2022_PROGRAM_ID,
    );

    console.log("Mint Account", mintAccount);
  });

  it("Create token account", async () => {
    const tx = await program.methods
      .createTokenAccount()
      .accounts({
        mint: mint,
        tokenProgram: TOKEN_2022_PROGRAM_ID,
      })
      .rpc({ commitment: "confirmed" });

    console.log("Your transaction signature", tx);

    const associatedTokenAccount = await getAssociatedTokenAddress(
      mint,
      program.provider.publicKey,
      false,
      TOKEN_2022_PROGRAM_ID,
    );

    const tokenAccount = await getAccount(
      program.provider.connection,
      associatedTokenAccount,
      "confirmed",
      TOKEN_2022_PROGRAM_ID,
    );

    console.log("Token Account", tokenAccount);
  });
});
```

</Tab>
</Tabs>

### Create Token Account using PDA

Create a token account using a Program Derived Address (PDA) as the address of
the token account.

<Tabs items={["Program", "Client"]}>
<Tab value="Program">

```rust title="lib.rs"
use anchor_lang::prelude::*;
use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface};

declare_id!("3pX5NKLru1UBDVckynWQxsgnJeUN3N1viy36Gk9TSn8d");

#[program]
pub mod token_example {
    use super::*;

    pub fn create_mint(ctx: Context<CreateMint>) -> Result<()> {
        msg!("Created Mint Account: {:?}", ctx.accounts.mint.key());
        Ok(())
    }

    pub fn create_token_account(ctx: Context<CreateTokenAccount>) -> Result<()> {
        msg!(
            "Created Token Account: {:?}",
            ctx.accounts.token_account.key()
        );
        Ok(())
    }
}

#[derive(Accounts)]
pub struct CreateMint<'info> {
    #[account(mut)]
    pub signer: Signer<'info>,
    #[account(
        init,
        payer = signer,
        mint::decimals = 6,
        mint::authority = mint.key(),
        mint::freeze_authority = mint.key(),
        seeds = [b"mint"],
        bump
    )]
    pub mint: InterfaceAccount<'info, Mint>,
    pub token_program: Interface<'info, TokenInterface>,
    pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct CreateTokenAccount<'info> {
    #[account(mut)]
    pub signer: Signer<'info>,
    #[account(
        // [!code highlight:7]
        init_if_needed,
        payer = signer,
        token::mint = mint,
        token::authority = token_account,
        token::token_program = token_program,
        seeds = [b"token"],
        bump
    )]
    pub token_account: InterfaceAccount<'info, TokenAccount>,
    pub mint: InterfaceAccount<'info, Mint>,
    pub token_program: Interface<'info, TokenInterface>,
    pub system_program: Program<'info, System>,
}
```

</Tab>
<Tab value="Client">

```ts title="test.ts"
import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { TokenExample } from "../target/types/token_example";
import {
  TOKEN_2022_PROGRAM_ID,
  getAssociatedTokenAddress,
  getMint,
  getAccount,
} from "@solana/spl-token";

describe("token-example", () => {
  anchor.setProvider(anchor.AnchorProvider.env());

  const program = anchor.workspace.TokenExample as Program<TokenExample>;
  const [mint, mintBump] = anchor.web3.PublicKey.findProgramAddressSync(
    [Buffer.from("mint")],
    program.programId,
  );

  const [token, tokenBump] = anchor.web3.PublicKey.findProgramAddressSync(
    [Buffer.from("token")],
    program.programId,
  );

  it("Is initialized!", async () => {
    const tx = await program.methods
      .createMint()
      .accounts({
        tokenProgram: TOKEN_2022_PROGRAM_ID,
      })
      .rpc({ commitment: "confirmed" });
    console.log("Your transaction signature", tx);

    const mintAccount = await getMint(
      program.provider.connection,
      mint,
      "confirmed",
      TOKEN_2022_PROGRAM_ID,
    );

    console.log("Mint Account", mintAccount);
  });

  it("Create token account", async () => {
    const tx = await program.methods
      .createTokenAccount()
      .accounts({
        mint: mint,
        tokenProgram: TOKEN_2022_PROGRAM_ID,
      })
      .rpc({ commitment: "confirmed" });

    console.log("Your transaction signature", tx);

    const tokenAccount = await getAccount(
      program.provider.connection,
      token,
      "confirmed",
      TOKEN_2022_PROGRAM_ID,
    );

    console.log("Token Account", tokenAccount);
  });
});
```

</Tab>
</Tabs>
